package org.example.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.example.annotation.FeignRetry;
import org.springframework.retry.RetryPolicy;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author WN
 * @date：2024/03/22 15:50
 */
@Aspect
@Slf4j
@Component
public class FeignRetryAspect {

    /**
     * 对标注了 FeignRetry 注解的方法进行重试处理。
     *
     * @param joinPoint 切面连接点，表示当前被拦截的方法。
     * @return 返回方法执行的结果。
     * @throws Throwable 如果方法执行或重试过程中出错，则抛出异常。
     */
    @Around("@annotation(org.example.annotation.FeignRetry)")
    public Object retry(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取当前执行的方法
        Method method = getCurrentMethod(joinPoint);
        // 获取方法注解
        FeignRetry feignRetry = method.getAnnotation(FeignRetry.class);

        RetryTemplate retryTemplate = new RetryTemplate();
        // 配置重试策略
        retryTemplate.setBackOffPolicy(prepareBackOffPolicy(feignRetry));
        // 配置最大重试次数
        retryTemplate.setRetryPolicy(prepareSimpleRetryPolicy(feignRetry));

        // arg0为RetryContext的实例，包含了请求重试信息
        return retryTemplate.execute(arg0 -> {
            // 执行方法并进行重试
            int retryCount = arg0.getRetryCount();
            log.info("send request 调用方法: {}, 最大重试次数:{}, 第一次重试前的等待时间:{}, 重试次数:{}",
                    method.getName(), feignRetry.maxAttempt(), feignRetry.backoff().delay(), retryCount);
            return joinPoint.proceed(joinPoint.getArgs());
        }, context -> {
            // 处理重试失败的情况
            log.error("retry count:{}", context.getRetryCount());
            return "fail callback";
        });
    }

    /**
     * 准备简单的重试策略。
     *
     * @param feignRetry FeignRetry注解实例，用于获取配置参数。
     * @return 返回配置好的 SimpleRetryPolicy 实例。
     */
    private RetryPolicy prepareSimpleRetryPolicy(FeignRetry feignRetry) {
        SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
        simpleRetryPolicy.setMaxAttempts(feignRetry.maxAttempt());
        return simpleRetryPolicy;
    }

    /**
     * 准备简单的重试策略。
     *
     * @param feignRetry FeignRetry注解实例，用于获取配置参数。
     * @return 返回配置好的 SimpleRetryPolicy 实例。
     */
    private BackOffPolicy prepareBackOffPolicy(FeignRetry feignRetry) {
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(feignRetry.backoff().delay());
        backOffPolicy.setMultiplier(feignRetry.backoff().multiplier());
        backOffPolicy.setMaxInterval(feignRetry.backoff().maxDelay());
        return backOffPolicy;
    }

    /**
     * 获取当前执行的方法。
     *
     * @param joinPoint 切面连接点。
     * @return 返回当前执行的方法。
     */
    private Method getCurrentMethod(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        return methodSignature.getMethod();
    }
}
